using Oracle.ManagedDataAccess.Client;
using Polly;
using Polly.Retry;
using System.Data;

namespace Lzfy_His_Service.Services
{
    /// <summary>
    /// 具有弹性的Oracle连接工厂
    /// </summary>
    public class ResilientOracleConnectionFactory : IOracleConnectionFactory, IResilientDbConnectionFactory
    {
        private readonly string _connectionString;
        private readonly ILogger<ResilientOracleConnectionFactory> _logger;
        private readonly ResiliencePipeline _retryPipeline;
        private static readonly object _lock = new object();
        private static DateTime _lastPoolExhaustionTime = DateTime.MinValue;
        private static int _consecutivePoolErrors = 0;

        public ResilientOracleConnectionFactory(
            string connectionString,
            ILogger<ResilientOracleConnectionFactory> logger)
        {
            _connectionString = connectionString;
            _logger = logger;
            
            // 创建重试策略 (Polly v8)
            _retryPipeline = new ResiliencePipelineBuilder()
                .AddRetry(new RetryStrategyOptions
                {
                    ShouldHandle = new PredicateBuilder().Handle<OracleException>(ex => IsTransientError(ex))
                        .Handle<TimeoutException>(),
                    MaxRetryAttempts = 3, // 重试3次
                    DelayGenerator = args =>
                    {
                        // 指数退避策略
                        var delay = TimeSpan.FromSeconds(Math.Pow(2, args.AttemptNumber));
                        return new ValueTask<TimeSpan?>(delay);
                    },
                    OnRetry = args =>
                    {
                        _logger.LogWarning(args.Outcome.Exception, 
                            "Oracle连接失败，将在 {RetryTimeSpan} 后进行第 {RetryCount} 次重试。错误: {ErrorMessage}",
                            args.RetryDelay, args.AttemptNumber, args.Outcome.Exception?.Message);
                        return ValueTask.CompletedTask;
                    }
                })
                .Build();
        }

        /// <summary>
        /// 创建数据库连接
        /// </summary>
        public IDbConnection CreateConnection()
        {
            var connection = new OracleConnection(_connectionString);
            connection.InfoMessage += (sender, args) =>
            {
                _logger.LogInformation("Oracle信息消息: {Message}", args.Message);
            };
            return connection;
        }

        /// <summary>
        /// 创建并打开数据库连接（带重试机制）
        /// </summary>
        public async Task<IDbConnection> CreateAndOpenConnectionAsync()
        {
            try
            {
                return await _retryPipeline.ExecuteAsync(async _ =>
                {
                    var connection = CreateConnection() as OracleConnection;
                    if (connection == null)
                    {
                        throw new InvalidOperationException("无法创建 Oracle 连接");
                    }
                    await connection.OpenAsync();
                    return connection;
                });
            }
            catch (OracleException ex) when (ex.Number == 50012 || ex.Message.Contains("Pooled connection request timed out"))
            {
                HandlePoolExhaustion();
                throw new InvalidOperationException("数据库连接池已耗尽，请稍后重试", ex);
            }
        }

        /// <summary>
        /// 处理连接池耗尽情况
        /// </summary>
        private void HandlePoolExhaustion()
        {
            lock (_lock)
            {
                _consecutivePoolErrors++;
                _lastPoolExhaustionTime = DateTime.Now;
                
                // 获取连接池基本信息（通过连接字符串解析）
                try
                {
                    var connectionBuilder = new OracleConnectionStringBuilder(_connectionString);
                    var minPoolSize = connectionBuilder.MinPoolSize;
                    var maxPoolSize = connectionBuilder.MaxPoolSize;
                    var connectionTimeout = connectionBuilder.ConnectionTimeout;
                    
                    var poolInfo = $"连接池配置: 最小连接数={minPoolSize}, 最大连接数={maxPoolSize}, 连接超时={connectionTimeout}秒";
                    
                    if (_consecutivePoolErrors >= 5)
                    {
                        _logger.LogError("Oracle连接池连续耗尽 {_consecutivePoolErrors} 次。{PoolInfo}。考虑重启应用或增加连接池大小", 
                            _consecutivePoolErrors, poolInfo);
                        
                        // 强制清理连接池
                        OracleConnection.ClearAllPools();
                        _logger.LogInformation("已强制清理Oracle连接池");
                        
                        _consecutivePoolErrors = 0;
                    }
                    else
                    {
                        _logger.LogWarning("Oracle连接池耗尽，错误计数: {_consecutivePoolErrors}。{PoolInfo}", 
                            _consecutivePoolErrors, poolInfo);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "解析连接字符串失败: {ErrorMessage}", ex.Message);
                    
                    if (_consecutivePoolErrors >= 5)
                    {
                        _logger.LogError(ex, "Oracle连接池连续耗尽 {_consecutivePoolErrors} 次，考虑重启应用或增加连接池大小", _consecutivePoolErrors);
                        
                        OracleConnection.ClearAllPools();
                        _logger.LogInformation("已强制清理Oracle连接池");
                        
                        _consecutivePoolErrors = 0;
                    }
                    else
                    {
                        _logger.LogWarning(ex, "Oracle连接池耗尽，错误计数: {_consecutivePoolErrors}", _consecutivePoolErrors);
                    }
                }
            }
        }

        /// <summary>
        /// 获取连接池状态信息
        /// </summary>
        public static Dictionary<string, object> GetPoolStats()
        {
            return new Dictionary<string, object>
            {
                ["LastPoolExhaustionTime"] = _lastPoolExhaustionTime,
                ["ConsecutivePoolErrors"] = _consecutivePoolErrors,
                ["CurrentTime"] = DateTime.Now
            };
        }

        /// <summary>
        /// 判断是否为瞬态错误
        /// </summary>
        private bool IsTransientError(OracleException ex)
        {
            // Oracle瞬态错误代码列表
            int[] transientErrorNumbers =
            {
                // 连接错误
                12154, // TNS无法解析服务名
                12170, // 连接超时
                12500, // TNS监听程序无法启动专用服务器进程
                12505, // TNS监听程序不识别连接描述符中请求的SID
                12519, // TNS:没有可用的适当服务处理程序
                12520, // TNS:监听程序无法找到请求的SID中可用的处理程序
                12521, // TNS:监听程序不知道实例请求的服务处理程序
                12525, // TNS:监听程序无法获取实例的状态
                12535, // TNS:操作超时
                12547, // TNS:丢失连接
                12560, // TNS:协议适配器错误
                12571, // TNS:包写入器故障
                12606, // TNS:应用程序超时发生
                12637, // 包读取器故障
                12641, // 认证服务失败
                
                // 网络错误
                3113,  // 通信通道的文件结尾
                3114,  // 与Oracle通信时没有收到
                3135,  // 连接丢失
                
                // 资源限制
                1033,  // Oracle正在启动或关闭
                1034,  // Oracle未启动
                1089,  // 系统关闭立即生效
                1090,  // 系统正在关闭
                
                // 死锁和超时
                60,    // 死锁检测到
                4020,  // 超时发生
                4021,  // 超时发生
                30006, // 资源忙
                
                // 其他可重试错误
                24756, // 事务已回滚
                25408, // 无法生成唯一服务器组名称
                29276, // 转移连接失败
                24347, // 无法连接到目标实例
            };

            return transientErrorNumbers.Contains(ex.Number);
        }
    }
}